﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics.Contracts;
using HeapSpecSupport;
using SimpleIR;

namespace Evaluator.EvalState
{
	/// <summary>
	/// This class is used to represent object values (just maps of fields to types).
	/// </summary>
	internal abstract class EObject : ELocation
	{
		/// <summary>
		/// Allocation info for the object.
		/// </summary>
		protected readonly AllocRecord m_allocinfo;

		/// <summary>
		/// An array of entries for each of the fields that are defined in the structure.
		/// </summary>
		protected BAPLayoutMap m_fieldinfo;

		protected EObject(int address, IRClassType tt, AllocRecord alloc, BAPLayoutMap fvalues)
			: base(address, tt)
		{
			Contract.Requires(tt != null);
			Contract.Requires(fvalues != null);

			this.m_allocinfo = alloc;
			this.m_fieldinfo = fvalues;
		}

		/// <summary>
		/// Simulate the initialization on base() for the objects
		/// </summary>
		internal void SimulateObjectConstructor(ulong time, long line)
		{
			IRMemberField cf = ExeProgram.TProg.TypeTable.RootObjectRTTIField;
			IRBasicAccessPath cfp = ExeProgram.TProg.GetAccessPathForSingleField(cf);
			int tc = (int) (time & 0xFFFFFFF);
			int lc = (int) (line & 0xFFFFFFF);
			int rttiv = (tc) ^ (lc);

			this.DoSimpleValueWriteToLocation(BAPLayoutEntry.INVALID_ARRAY_INDEX, cfp, new ENumeric(ExeProgram.TProg.TypeTable.Int32Type, rttiv), time, line);
		}

		/// <summary>
		/// Given a type create the default value for it.
		/// </summary>
		internal static EObject AllocateBasicObjectOfType(List<ELocation> memory, IRClassType tt, ulong ctime, long line)
		{
			int address = memory.Count;
			EObject res = null;
			if(tt.TypeDescription.ExtendsSpecialImplType == null)
				res = EUserDefObject.CreateFreshObjectValueForType(address, tt, ctime, line);
			else
			{
				if(tt.TypeDescription.ExtendsSpecialImplType == ExeProgram.TProg.TypeTable.StringObjectType)
					res = EStringObject.CreateStringValue(address, tt, ctime, line);
				else if(tt.TypeDescription.ExtendsSpecialImplType is IRDictionaryType)
					res = EDictionaryObject.CreateFreshDictionaryValue(address, (IRDictionaryType)tt, ctime, line);
				else if(tt.TypeDescription.ExtendsSpecialImplType is IRContainerType)
				{
					IRContainerType cty = (IRContainerType)tt.TypeDescription.ExtendsSpecialImplType;

					if(cty.IsListContainer)
						res = EListObject.CreateFreshListValueForType(address, (IRContainerType)tt, ctime, line);
					else if(cty.IsSetBehaviorContainer)
						res = EHashSetObject.CreateFreshHashSetValueForType(address, (IRContainerType)tt, ctime, line);
					else
					{
						DebugReport.ReportError("Unknown specialized container: " + tt.ToString());
						throw new InvalidOperationException();
					}
				}
				else if(tt.TypeDescription.ExtendsSpecialImplType is IRLambdaType)
					res = EDelegateObject.CreateDelegateValue(address, (IRLambdaType)tt, ctime, line);
				else
				{
					DebugReport.ReportError("Unknown special type:" + tt.ToString());
					throw new InvalidOperationException();
				}
			}

			memory.Add(res);
			return res;
		}

		/// <summary>
		/// Return the class type that this object is.
		/// </summary>
		internal IRClassType ObjectType
		{ get { return (IRClassType)this.m_loctype; } }

		internal override EValue DoSimpleValueReadFromLocation(int arrayidx, IRBasicAccessPath bap, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			return this.m_fieldinfo.ReadFieldValue(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, time, line); 
		}

		internal override void DoSimpleValueWriteToLocation(int arrayidx, IRBasicAccessPath bap, EValue nval, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			this.m_fieldinfo.WriteFieldValue(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, (EMemoryValue)nval, time, line); 
		}

		internal override void DoCompoundReadFromLocation(int arrayidx, IRBasicAccessPath bap, BAPLayoutMap into, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			this.m_fieldinfo.ReadFieldValues(BAPLayoutEntry.INVALID_ARRAY_INDEX, bap, into, time, line); 
		}

		internal override void DoCompoundWriteToLocation(int arrayidx, IRBasicAccessPath bap, BAPLayoutMap from, ulong time, long line)
		{
			Contract.Assert(arrayidx == BAPLayoutEntry.INVALID_ARRAY_INDEX);
			Contract.Assert(bap != null);

			this.m_fieldinfo.WriteFieldValues(BAPLayoutEntry.INVALID_ARRAY_INDEX, from, bap, time, line); 
		}
	}
}
